import {
    MeshStandardMaterial,
    DoubleSide,
    Group,
    Mesh,
    BoxGeometry,
    Vector3,
    Object3D
} from "three";
import {
    App,
    ButtonGroupControl,
    DOM,
    Entity,
    EventModule,
    GeoBounds,
    GeoPoint,
    LoadManager,
    MeshModule,
    ResManager,
    closestPointOnPolyline,
    creataMeasureRuler,
    extrudePolyline,
    fromWorld,
    geoPoint,
    loadPluginAlgorithm,
    makeObject3DUiConfig,
    toDist,
    toWorld
} from "vjmap3d";
import { createRoutePath, exportModelToDwg } from "./util";
import { env } from "./env";
export const initButtonGroup = async (
    app: App,
    mapBounds: GeoBounds
) => {
    const amLib = await loadPluginAlgorithm();
    const evaluator = new amLib.Evaluator();
    const material = new MeshStandardMaterial({
        color: "#D9FFF8",
        side: DoubleSide
    });

    let mapW = mapBounds.width();
    let ratio = mapW / 8192; // 因为下面的距离是按模板图t84091e2d5fa（模板图宽是8192）来的，如果换成其他的图，需要乘以一个比例

    // 所有的模型放至一个组里面，用于方便下面的导出模型为cad图
    let groupModel = new Group();
    app.scene.add(groupModel);
    // 所有的墙放至一个组里面
    let wallGroup = new Group();
    app.scene.add(wallGroup);

    const wallMeshs: any = [];
    const drawWall = async (points: number[][]) => {
        points = points.map(p => [p[0], app.isMapMode ? p[1] : p[2]]);

        let { geometry } = extrudePolyline([points], {
            depth: toDist(300 * ratio) /** cad长度转三维世界坐标长度 */,
            lineWidth: toDist(26 * ratio) /** cad长度转三维世界坐标长度 */
        });

        const mesh = new Mesh(geometry, material);
        wallGroup.add(mesh);
        wallMeshs.push({
            mesh: mesh,
            // @ts-ignore
            brush: new amLib.Brush(geometry, material), // 不用显示，用于与门或窗相交做布尔运算
            // @ts-ignore
            points: points.map(p => geoPoint(p))
        });
    };

    if (env.mapId == "t84091e2d5fa") {
        // 如果是示例的图，则加入初始的数据
        // 开始时绘制墙
        let initWallPoints = [
            [237.7131669947512, -1536.7813727466014],
            [237.31675351102285, -447.00787369573754],
            [489.7703752013481, -444.06145145076607],
            [491.4109377585164, -323.65966687690343],
            [1292.4951183389594, -324.692894018116],
            [1293.0111353419143, -235.4237686547549],
            [1859.8922398267414, -237.25271540975882],
            [1867.819691385991, -1050.2752874416515],
            [2160.969358821808, -1053.9723392575102],
            [2156.229008753362, -1427.460243179451],
            [1756.5971668821812, -1423.9844308627926],
            [1752.589851827045, -1520.52668712574],
            [1298.4798067484853, -1518.3636797648378],
            [1300.1050653455059, -1567.3669655033236],
            [991.047700008301, -1565.669775580734],
            [985.4601998907356, -1538.3710731323526],
            [237.7131669947512, -1536.7813727466014]
        ];
        initWallPoints = initWallPoints.map(p => {
            // @ts-ignore
            let pt = toWorld(p); // 转三维场景中的世界坐标
            return pt.toArray();
        });
        drawWall(initWallPoints);
    }

    const addObject = async (mesh: Mesh, posY?: number) => {
        let result: any;
        let oldFindWall: any;
        let findWall: any;
        let oldQuat = mesh.quaternion.clone();
        app.container.style.cursor = "crosshair";
        //app.map.getCanvas().style.cursor = "crosshair";
        let drawPoint = await app.actionDrawPoint({
            updateCoordinate: point => {
                let pt = new GeoPoint(point.x, app.isMapMode ? point.y : point.z);
                let minDist: number = Number.MAX_VALUE;
                let closedPoint;
                let angle = 0;

                // 根据当前鼠标位置找一个最近墙，算出当前的距离和方向
                for (let n = 0; n < wallMeshs.length; n++) {
                    if (n == 0) {
                        findWall = wallMeshs[0];
                        let r = closestPointOnPolyline(pt, wallMeshs[0].points);
                        if (r && r.closestPoint) {
                            closedPoint = r.closestPoint;
                            minDist = r.closestLength;
                            angle = -closedPoint.angleTo(wallMeshs[0].points[r.closestIndex]);
                        }
                    } else {
                        let r = closestPointOnPolyline(pt, wallMeshs[n].points);
                        if (r && r.closestLength < minDist && r.closestPoint) {
                            closedPoint = r.closestPoint;
                            minDist = r.closestLength;
                            findWall = wallMeshs[n];
                            angle = -closedPoint.angleTo(wallMeshs[n].points[r.closestIndex]);
                        }
                    }
                }
                if (closedPoint) {
                    if (oldFindWall) {
                        oldFindWall.mesh.visible = true;
                    }
                    // 先隐藏原来的
                    findWall.mesh.visible = false;
                    mesh.position.set(
                        closedPoint.x,
                        app.isMapMode ? closedPoint.y : posY ?? 0,
                        app.isMapMode ? posY ?? 0 : closedPoint.y
                    );
                    mesh.quaternion.copy(oldQuat);
                    if (!app.isMapMode) {
                        mesh.rotateY(angle);
                    } else {
                        mesh.rotateZ(angle);
                    }

                    if (result) {
                        result.geometry.dispose();
                        result.parent.remove(result);
                    }
                    mesh.updateMatrixWorld();

                    // 用克隆的与门或窗相交做布尔运算，结果加进场景中
                    result = evaluator.evaluate(findWall.brush, mesh as any, amLib.HOLLOW_SUBTRACTION);
                    wallGroup.add(result);
                    oldFindWall = findWall;
                }
            }
        });
        if (drawPoint.isCancel) {
            // 取消了
            if (result) {
                result.geometry.dispose();
                result.parent.remove(result);
            }
            if (findWall) {
                findWall.mesh.visible = true;
            }
            return;
        }
        if (result) {
            if (findWall && findWall.mesh) {
                findWall.mesh.geometry.dispose();
                findWall.brush.geometry.dispose();
                findWall.mesh.parent.remove(result);
            }
            if (result) {
                result.parent?.remove(result);
            }
            findWall.mesh = new Mesh(result.geometry, material);
            // @ts-ignore
            findWall.brush = new amLib.Brush(result.geometry, material); // 不用显示，克隆一个用于与门或窗相交做布尔运算
            wallGroup.add(findWall.mesh);
        }
    };
    let control = new ButtonGroupControl({
        buttons: [
            {
                id: "drawWall",
                html: "绘制墙体",
                title: "绘制墙体",
                style: {
                    width: "100px"
                },
                onclick: async () => {
                    let line = await app.actionDrawLineSting();
                    if (line.isCancel) return;
                    drawWall(line.data.coordinates);
                }
            },
            {
                id: "drawDoor",
                html: "绘制门",
                title: "绘制门",
                style: {
                    width: "100px"
                },
                onclick: async () => {
                    let w = toDist(100 * ratio);
                    let h = !app.isMapMode ? toDist(250 * ratio) : toDist(55 * ratio); // 地图模式时，z轴向上；纯三维模式时，y轴向上，所以d,h要互换下
                    let d = !app.isMapMode ? toDist(55 * ratio) : toDist(250 * ratio);
                    let y = toDist(120 * ratio);
                    // @ts-ignore
                    const door = new amLib.Brush(new BoxGeometry(w, h, d));
                    // @ts-ignore
                    await addObject(door, y);
                }
            },
            {
                id: "drawWindow",
                html: "绘制窗户",
                title: "绘制窗",
                style: {
                    width: "100px"
                },
                onclick: async () => {
                    let w = toDist(200 * ratio);
                    let h = !app.isMapMode ? toDist(190 * ratio) : toDist(55 * ratio); // 地图模式时，z轴向上；纯三维模式时，y轴向上，所以d,h要互换下
                    let d = !app.isMapMode ? toDist(55 * ratio) : toDist(190 * ratio);
                    let y = toDist(150 * ratio);
                    // @ts-ignore
                    const window = new amLib.Brush(new BoxGeometry(w, h, d));
                    // @ts-ignore
                    await addObject(window, y);
                }
            },
            {
                id: "heatmap",
                html: "温度热力图分析",
                title: "",
                style: {
                    width: "110px"
                },
                onclick: async () => {
                    // 因为墙体有可能会增加，可以先取消之前的，再重新关联下
                    // 先取消之前的高亮
                    let meshEnt = Entity.attchObject(wallGroup, true);
                    meshEnt.getMeshEntities().forEach(async (ent: Entity) => {
                        let mod = ent.getModule(MeshModule);
                        mod.resetMaterial();
                    });

                    // 重新关联实体,设置热力图
                    let entity = Entity.attchObject(wallGroup, true, true);
                    let box3d = entity.getBoundingBox(false);

                    let pointNum = 10;
                    let data: any = [];
                    let size = new Vector3();
                    box3d.getSize(size);
                    for (let k = 0; k < pointNum; k++) {
                        data.push({
                            x: box3d.min.x + Math.random() * size.x,
                            y: box3d.min.y + Math.random() * size.y,
                            z: box3d.min.z + Math.random() * size.z,
                            value: Math.random() * 5 + 20
                        });
                    }

                    entity.getMeshEntities().forEach(async (ent: Entity) => {
                        let mod = ent.getModule(MeshModule);
                        await mod.setHeatmapDataMaterial({
                            data
                        });
                    });
                }
            },
            {
                id: "heatmapcancel",
                html: "取消热力图分析",
                title: "",
                style: {
                    width: "110px"
                },
                onclick: async () => {
                    let entity = Entity.attchObject(wallGroup, true);
                    entity.getMeshEntities().forEach(async (ent: Entity) => {
                        let mod = ent.getModule(MeshModule);
                        mod.resetMaterial();
                    });
                }
            },
            {
                id: "adddoor",
                html: "放置门",
                title: "",
                style: {
                    width: "110px"
                },
                onclick: async () => {
                    addModelToApp(
                        app,
                        groupModel,
                        "puerta/puerta.obj",
                        "puerta/puerta.mtl",
                        240 * ratio,
                        "门"
                    );
                }
            },
            {
                id: "addwindow",
                html: "放置窗",
                title: "",
                style: {
                    width: "110px"
                },
                onclick: async () => {
                    addModelToApp(
                        app,
                        groupModel,
                        "doubleWindow2/doubleWindow2.obj",
                        "doubleWindow2/doubleWindow2.mtl",
                        220 * ratio,
                        "窗",
                        60 * ratio
                    );
                }
            },
            {
                id: "addbed",
                html: "放置床",
                title: "",
                style: {
                    width: "110px"
                },
                onclick: async () => {
                    addModelToApp(
                        app,
                        groupModel,
                        "bed/bed3.obj",
                        "bed/bed3.mtl",
                        300 * ratio,
                        "床"
                    );
                }
            },
            {
                id: "sofa2",
                html: "放置沙发",
                title: "",
                style: {
                    width: "110px"
                },
                onclick: async () => {
                    addModelToApp(
                        app,
                        groupModel,
                        "sofa2/sofa2.obj",
                        "sofa2/sofa2.mtl",
                        400 * ratio,
                        "沙发"
                    );
                }
            },
            {
                id: "table",
                html: "放置桌子",
                title: "",
                style: {
                    width: "110px"
                },
                onclick: async () => {
                    addModelToApp(
                        app,
                        groupModel,
                        "table/table.obj",
                        "table/table.mtl",
                        240 * ratio,
                        "桌子"
                    );
                }
            },
            {
                id: "toiletUnit",
                html: "放置马桶",
                title: "",
                style: {
                    width: "110px"
                },
                onclick: async () => {
                    addModelToApp(
                        app,
                        groupModel,
                        "toiletUnit/toiletUnit.obj",
                        "",
                        160 * ratio,
                        "马桶"
                    );
                }
            },
            {
                id: "customModel",
                html: "放置自定义模型",
                title: "",
                style: {
                    width: "110px"
                },
                onclick: async () => {
                    app.logInfo("请选择本地的模型文件，拖放至场景中相应位置", "success", 5000)
                }
            },
            {
                id: "editpos",
                html: "编辑物体位置",
                title: "",
                style: {
                    width: "110px"
                },
                onclick: async () => {
                    app.setCurrentTool("transform", {
                        mode: "translate",
                        showX: ["translate", "scale"], // 平移和缩放时显示x轴
                        showY: app.isMapMode ? ["translate", "scale"] : ["rotate", "scale"],
                        showZ: !app.isMapMode ? ["translate", "scale"] : ["rotate", "scale"],
                        clickNoEntityExit: true
                    });
                    showMessageTip(app);
                }
            },
            {
                id: "editprop",
                html: "编辑物体属性",
                title: "",
                style: {
                    width: "110px"
                },
                onclick: async () => {
                    let opts = {
                        highlightUseBoxHelper: true,
                        highlightOpacity: 0,
                        pickCallBack: async (param: any) => {
                            const ui = await app.getConfigPane({
                                style: {
                                    right: "160px"
                                }
                            })
                            ui.reset();
                            if (param.curPick) {
                                ui.setVisible(true)
                                let config;
                                if (app.Input.getKeyPressed("ControlLeft") && param.curPick.intersection?.object) {
                                    // 如果按往了ctrl键，则只选择中当前选择的
                                    let obj = param.curPick.intersection?.object;
                                    config = makeObject3DUiConfig(obj)
                                } else {
                                    config = param.curPick.target.uiConfig;
                                }
                                ui.appendChild(config)
                            } else {
                                ui.setVisible(false)
                                // 退出
                                app.setCurrentTool("")
                            }
                        }
                    }
                    app.setCurrentTool("pick", opts)
                }
            },
            {
                id: "routepath",
                html: "浏览线路",
                title: "",
                style: {
                    width: "110px"
                },
                onclick: async () => {
                    createRoutePath(app, toDist(20 * ratio));
                }
            },
            {
                id: "measureruler",
                html: "测量标尺",
                title: "",
                style: {
                    width: "110px"
                },
                onclick: async () => {
                    let line = await app.actionDrawLineSting({
                        pointMaxCount: 2
                    });
                    creataMeasureRuler(app, {
                        startPoint: line.data.coordinates[0],
                        endPoint: line.data.coordinates[1],
                        // @ts-ignore
                        textStyle: {
                            billboard: false
                        }
                    });
                }
            },
            {
                id: "cadmap",
                html: "根据模型生成CAD平面图",
                title: "",
                style: {
                    width: "110px"
                },
                onclick: async () => {
                    showMessageTip(app, "正在正成CAD平面图, 请稍候...");
                    let entities: any[] = [];
                    // 要绘制的墙也导出
                    wallMeshs.forEach((w: any) => {
                        entities.push({
                            typename: "DbPolyline",
                            colorIndex: 4,
                            points: w.points.map((p: any) => {
                                let pt = fromWorld(p.toArray());
                                return [pt[0], pt[1]];
                            })
                        });
                    });
                    exportModelToDwg(app, groupModel, entities);
                }
            }
        ]
    });
    app.addControl(control, "top-right");

    enableDragModel(app)


let control2 = new ButtonGroupControl({
    buttons: [
        {
            id: 'reset-settings',
            html: !app.isMapMode ? "2D图" : "3D图",
            title: !app.isMapMode ? "以2D图3D做为图层的形式打开查看" : '以3D形式打开查看',
            style: { width: "40px"},
            onclick: async() => {
               let href = window.location.href;
               if (href.indexOf("?") == -1)href += "?"
               href = href.replace("&map2d=true", "");
               href = href.replace("&map2d=false", "");
               href = href.replace("map2d=true", "");
               href = href.replace("map2d=false", "")
               if (app.isMapMode) {
                href += "map2d=false"
               } else {
                href += "map2d=true"
               }
               window.open(href)
            },
        },
        {
            id: 'auto3d',
            html: "生成三维模型",
            title: "根据图中的三维多段线、多面网格数据自动生成三维模型",
            style: { width: "100px"},
            onclick: async() => {
               let href = window.location.href;
               if (href.indexOf("?") == -1)href += "?"
               href  = href.replace("/design3d/", "/terrain/")
               href += "&show3dpolyline=true&show3dpolymesh=true&show3dpolyface=false&scaleZ=1&baseZ=1" 
               window.open(href)
            }
        }
    ],
});
app.addControl(control2, "top-left")
};

// 给地图增加一个模型
const addModelToApp = async (
    app: App,
    parent: Object3D,
    modelPath: string,
    mtlPath: string,
    size: number,
    name: string,
    y?: number
) => {
    size = toDist(size);
    if (y) {
        y = toDist(y);
    }
    if (mtlPath) {
        // 如果是obj，需要先加载mtl文件
        let materials = await ResManager.loadRes("./models/" + mtlPath, true);
        materials.preload();
        let loader = LoadManager.getLoader("obj");
        // @ts-ignore
        loader.setMaterials(materials);
    }

    let ent = await ResManager.loadModel("./models/" + modelPath, {
        size: size,
        position: new Vector3(0, y ?? 0, 0),
        splitSubEntity: true,
        useCache: true
    });
    ent.addTo(parent, app);

    if (mtlPath) {
        // @ts-ignore
        LoadManager.getLoader("obj")?.setMaterials(null);
    }

    let res = await app.actionDrawPoint({
        unprojectOpts: false,
        updateCoordinate: point => {
            if (!point) return;
            if (app.isMapMode) {
                point.z = y ?? 0;
            } else {
                point.y = y ?? 0;
            }
            ent.position.copy(point);
            return;
        }
    });
    if (res.isCancel) {
        ent.remove();
        return;
    }

    app.transformObject({
        target: ent.node,
        mode: "rotate",
        showX: ["translate", "scale"], // 平移和缩放时显示x轴
        showY: app.isMapMode ? ["translate", "scale"] : ["rotate", "scale"],
        showZ: !app.isMapMode ? ["translate", "scale"] : ["rotate", "scale"]
    });

    showMessageTip(app);

    ent.add(EventModule, {
        hoverSelect: true,
        hoverHtmlText: name,
        popupOptions: {
            anchor: "bottom"
        }
    });
};

export const showMessageTip = (app: App, tip?: string) => {
    let div = document.createElement("div");
    let message =
        tip ??
        "按 W 键切换平移模式, E 键切换旋转模式, R 键切换缩放模式, shfit 键时进行微调,  X Y Z 键分别对此轴进行显示或隐藏";
    div.innerHTML = `<h4 style = "position:absolute;color:${env.darkTheme ? "yellow" : "blue"};top:30px;left:100px">${message}</h4>`;
    app.container.appendChild(div);
    setTimeout(() => DOM.remove(div), 5000);
};


// 拖放一个模型至场景中
export const enableDragModel = async (app: App) => {
    // 支持拖放
    const dropZone = app.container;
    // Prevent default drag behaviors
    ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
        dropZone.addEventListener(eventName, preventDefaults, false);
    });

    function preventDefaults(e: any) {
        e.preventDefault();
        e.stopPropagation();
    }

    // Highlight drop area when item is dragged over it
    ['dragenter', 'dragover'].forEach(eventName => {
        dropZone.addEventListener(eventName, () => {
            dropZone.classList.add('dragover');
        }, false);
    });

    ['dragleave', 'drop'].forEach(eventName => {
        dropZone.addEventListener(eventName, () => {
            dropZone.classList.remove('dragover');
        }, false);
    });

    // Handle dropped files
    dropZone.addEventListener('drop', handleDrop, false);

    async function handleDrop(e: any) {
        const dt = e.dataTransfer;
        const files = dt.files;

        // 获取文件类型后缀
        let ext = files[0].name.substring(files[0].name.indexOf(".") + 1).toLowerCase()
        // Only handle the first file for this example
        if (files.length > 0) {
            const file = files[0];
            const url = URL.createObjectURL(file);
            
            let ent = await ResManager.loadModel(url, {
                splitSubEntity: true,
                size:  5,
                //anchor: "front-bottom",
                position: app.unproject(e.x, e.y), // 当前拖放的位置
                fileType: ext
            });
            ent.addTo(app);

            // 属性面板
            const ui = await app.getConfigPane({
                style: {
                    right: "160px"
                }
            })
            ui.setVisible(true);
            let config = makeObject3DUiConfig(ent.node)
            ui.appendChild(config)

            // 编辑
            let options = {
                target: ent.node,
                clickNoSelfEntityExit: true,
                clickNoEntityExit: true, 
                rightClickConfirmExit: true // 右键退出编辑
            }
            await app.transformObject(options)

            ui.setVisible(false);
        }
    } 
}